/** Pacotes que precisam ser importados para que suas funcionalidades sejam utilizadas no aplicativo */
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.LineBorder;

/**
 * Classe que implementa o aplicativo
 * 
 * @author Márcio Rocha e Rebecca Pereira
 * 
 */
public class Aplicativo {

	/** Cria uma nova instância de caixa de texto para receber a pesquisa */
	public static JTextField caixa = new JTextField();

	/**
	 * Cria uma nova instância de área de texto para retornar o resultado da
	 * pesquisa
	 */
	public static JTextArea texto = new JTextArea();

	/** Cria uma nova instância de barra de rolagem */
	public static JScrollPane scroll = new JScrollPane();

	/** Cria uma nova instância de frame */
	public static JFrame frame = new JFrame("BD relacional com Pub/Sub");

	/** Método que cria a tela de interação do usuário com o aplicativo */
	public static void tela() {

		/** Configuração das propriedades da caixa de texto */
		caixa.setBounds(25, 50, 330, 20);
		caixa.setBackground(Color.WHITE);
		caixa.setBorder(new LineBorder(Color.BLACK, 1));

		/** Configuração das propriedades da barra de rolagem */
		scroll.getViewport().setView(texto);
		scroll.setBounds(25, 120, 445, 150);
		scroll.setBorder(new LineBorder(Color.BLACK, 1));

		/** Cria uma nova instância de rótulo */
		JLabel label = new JLabel("Digite abaixo a consulta desejada:");
		label.setBounds(25, 20, 200, 20);

		/** Cria uma nova instância de botão */
		JButton botao = new JButton("Enviar");
		botao.setBounds(380, 50, 90, 20);

		/**
		 * Ação tomada pelo clique do botão para enviar a pesquisa para ser
		 * processada
		 */
		botao.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					/**
					 * Tenta chamar o método que envia a pesquisa, passando
					 * como parâmetro o texto contido na caixa de texto
					 */
					publishUsuario(caixa.getText());
				} catch (Exception x) {
					/** Se a tentativa falhar, exibe o erro */
					System.err.print(x.getMessage());
				}
			}
		});

		/** Cria uma nova instância de rótulo */
		JLabel label2 = new JLabel("Resultado:");
		label2.setBounds(25, 90, 200, 20);

		/** Cria uma nova instância de botão */
		JButton sair = new JButton("Sair");
		sair.setBounds(380, 300, 90, 20);

		/** Ação tomada pelo clique do botão para sair da tela */
		sair.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					/** Tenta sair do aplicativo */
					System.exit(0);
				} catch (Exception x) {
					/** Se a tentativa falhar, exibe o erro */
					System.err.print(x.getMessage());
				}
			}
		});

		/**
		 * Cria uma nova instância de painel para acoplar os objetos declarados
		 * anteriormente
		 */
		JDesktopPane jpane = new JDesktopPane();
		jpane.add(label);
		jpane.add(label2);
		jpane.add(caixa);
		jpane.add(botao);
		jpane.add(sair);
		jpane.add(scroll);
		jpane.setBackground(Color.LIGHT_GRAY);

		/** Configuração das propriedades do frame */
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(500, 400);
		frame.setLocationRelativeTo(null);
		frame.add(jpane);
		frame.setVisible(true);
	}

	/**
	 * Método que estabelece a conexão com o banco de dados PostgreSQL chamado
	 * projeto
	 * 
	 * @return conexão estabelecida
	 */
	public static Connection estabeleceConexao() {

		/** Variáveis necessárias para estabelecer a conexão */
		String driver = "org.postgresql.Driver";
		String usuario = "postgres";
		String senha = "postgres";
		String url = "jdbc:postgresql://localhost:5432/projeto";

		/**
		 * Tenta estabelecer a conexão e, se conseguir, a retorna na variável
		 * "conn"
		 */
		try {
			Class.forName(driver);
			Connection conn = null;
			conn = (Connection) DriverManager
					.getConnection(url, usuario, senha);
			return conn;
		}
		/**
		 * Caso não consiga estabelecer a conexão, exibe o erro e retorna nulo
		 */
		catch (Exception ex) {
			System.err.print(ex.getMessage());
			return null;
		}
	}

	/** Método que executa a pesquisa no banco de dados e prepara o resultado */
	public static void consulta(String query) throws NamingException,
			JMSException {

		/** Recebe a variável de conexão com o banco de dados */
		Connection con = estabeleceConexao();
		Statement stmt;

		/** Variável que irá receber o registro consultado */
		String buffer = "";

		/** Tenta recuperar o resultado da consulta e colocá-la numa string */
		try {
			stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery(query);
			ResultSetMetaData rsmd = rs.getMetaData();
			int numberOfColumns = rsmd.getColumnCount();
			while (rs.next()) {
				for (int i = 1; i <= numberOfColumns; i++) {
					if (i != 1) {
						buffer += ",";
					}
					buffer += rsmd.getColumnName(i) + ":";
					buffer += rs.getString(i);
				}
				/** Envia o resultado da consulta para ser publicado no tópico */
				publishBanco(buffer);
				buffer = "";
			}
			/** Fecha a conexão com o banco de dados */
			stmt.close();
			con.close();
		}
		/** Se não conseguir executar a query, retorna o erro */
		catch (SQLException e) {
			System.err.print(e.getMessage());
			// JOptionPane.showConfirmDialog(null, e.getMessage(),
			// "Digite uma tabela válida", JOptionPane.WARNING_MESSAGE);
			// JOptionPane.showConfirmDialog()
		}

		/** Coloca novamente o banco de dados em modo espera */
		subscriberBanco();
	}

	/** Método que escuta a pergunta do usuário */
	public static void subscriberBanco() throws NamingException, JMSException {

		/** Instancia um serviço de nomes - JNDI */
		InitialContext ic = new InitialContext();

		/** Acesso à fábrica de conexões JMS */
		TopicConnectionFactory factory = (TopicConnectionFactory) ic
				.lookup("jms/Fabrica");

		/** Inscrição no tópico JMS */
		Topic topic = (Topic) ic.lookup("jms/tabelas");

		/** Estabelece a conexão JMS */
		TopicConnection connection = factory.createTopicConnection();

		/** Cria uma sessão JMS */
		Session session = connection.createSession(false,
				Session.AUTO_ACKNOWLEDGE);

		// Variável que irá comsumir as mensagens
		MessageConsumer receiver = session.createConsumer(topic,
				"(modo = 'pergunta')");

		// Inicializa a conexão
		connection.start();

		// Aguardando mensagens
		System.out.println("Banco aguardando mensagens...");

		// Recebendo as mensagens
		TextMessage message = (TextMessage) receiver.receive();

		String query = message.getText();

		// Fechando a conexão
		receiver.close();
		session.close();
		connection.close();

		// Envia a pesquisa para ser consultada no banco de dados
		consulta(query);

	}

	public static void publishBanco(String mensagem) throws NamingException,
			JMSException {

		// Instancia um serviço de nomes - JNDI
		InitialContext ic = new InitialContext();

		// Acesso à fábrica de conexões JMS
		TopicConnectionFactory factory = (TopicConnectionFactory) ic
				.lookup("jms/Fabrica");

		// Inscrição no tópico JMS
		Topic topic = (Topic) ic.lookup("jms/tabelas");

		// Estabelece a conexão JMS
		TopicConnection connection = factory.createTopicConnection();

		// Cria uma sessão JMS
		Session session = connection.createSession(false,
				Session.AUTO_ACKNOWLEDGE);

		// Emissor de mensagens para o tópico em que está inscrito
		MessageProducer sender = session.createProducer(topic);

		// Conteúdo da mensagem enviada ao tópico
		TextMessage message = session.createTextMessage();
		message.setStringProperty("modo", "resposta");
		message.setText(mensagem);

		// Enviando a mensagem ao tópico
		sender.send(message);

		// fechando
		sender.close();
		session.close();
		connection.close();

	}

	public static void subscriberUsuario() throws NamingException, JMSException {

		// Instancia um serviço de nomes - JNDI
		InitialContext ic = new InitialContext();

		// Acesso à fábrica de conexões JMS
		TopicConnectionFactory factory = (TopicConnectionFactory) ic
				.lookup("jms/Fabrica");

		// Inscrição no tópico JMS
		Topic topic = (Topic) ic.lookup("jms/tabelas");

		// Estabelece a conexão JMS
		TopicConnection connection = factory.createTopicConnection();

		// Cria uma sessão JMS
		Session session = connection.createSession(false,
				Session.AUTO_ACKNOWLEDGE);

		// Variável que irá comsumir as mensagens
		MessageConsumer receiver = session.createConsumer(topic,
				"(modo = 'resposta')");

		// Inicializa a conexão
		connection.start();

		// Aguardando mensagens
		System.out.println("Usuário aguardando mensagens...");

		// Recebendo as mensagens
		TextMessage message = (TextMessage) receiver.receive();

		while (message != null) {
			String query = message.getText();
			texto.append(query);
			texto.append("\n");
			message = (TextMessage) receiver.receive(1000);
		}

		// fechando
		receiver.close();
		session.close();
		connection.close();

	}

	public static void publishUsuario(String mensagem) throws NamingException,
			JMSException {

		// Instancia um serviço de nomes - JNDI
		InitialContext ic = new InitialContext();

		// Acesso à fábrica de conexões JMS
		TopicConnectionFactory factory = (TopicConnectionFactory) ic
				.lookup("jms/Fabrica");

		// Inscrição no tópico JMS
		Topic topic = (Topic) ic.lookup("jms/tabelas");

		// Estabelece a conexão JMS
		TopicConnection connection = factory.createTopicConnection();

		// Cria uma sessão JMS
		Session session = connection.createSession(false,
				Session.AUTO_ACKNOWLEDGE);

		// Emissor de mensagens para o tópico em que está inscrito
		MessageProducer sender = session.createProducer(topic);

		// Conteúdo da mensagem enviada ao tópico
		TextMessage message = session.createTextMessage();
		message.setStringProperty("modo", "pergunta");
		message.setText(mensagem);

		// Enviando a mensagem ao tópico
		sender.send(message);

		// fechando
		sender.close();
		session.close();
		connection.close();

		// Limpa a textarea
		texto.setText("");

		// Coloca o usuário para ficar no modo espera
		subscriberUsuario();
	}

	public static void main(String[] args) throws NamingException, JMSException {
		tela();
		subscriberBanco();

	}

}
